package org.linzhu.Connect4.core;

/**
 *
 * @author zhulin
 */
public class Connect4GameImpl implements Connect4Game {

    private static final int COLUMN_SIZE = 7;
    private static final int ROW_SIZE = 6;

    // column index increases from left to right
    // row index increases from top to bottom
    private Player[][] board = new Player[COLUMN_SIZE][ROW_SIZE];
    private Status status = Status.Ongoing;

    public int getColumnCount() {
        return COLUMN_SIZE;
    }

    public int getRowCount() {
        return ROW_SIZE;
    }

    public Player getPlayerAt(int columnIndex, int rowIndex) {
        return board[columnIndex][rowIndex];
    }

    public Status getStatus() {
        return status;
    }

    public int dropMarble(Player player, int columnIndex) throws Connect4Exception {
        if (status == Status.Ongoing) {
            Player[] column = board[columnIndex];

            // check column not full
            if (column[0] == null) {
                // drop the marble in the largest empty row
                int rowIndex = 0;
                while (rowIndex < ROW_SIZE - 1 && column[rowIndex + 1] == null) {
                    rowIndex++;
                }
                column[rowIndex] = player;

                if (isHorizontalConnect4(player, columnIndex, rowIndex) ||
                        isVerticalConnect4(player, columnIndex, rowIndex) ||
                        isLeftDiagonalConnect4(player, columnIndex, rowIndex) ||
                        isRightDiagonalConnect4(player, columnIndex, rowIndex)) {
                    // the move makes this player win the game
                    status = (player == Player.Player1) ? Status.PlayerOneWin : Status.PlayerTwoWin;
                } else if (isAllColumnFull()) {
                    // the game is a draw
                    status = Status.Draw;
                } else {
                    // the game keeps on going
                    status = Status.Ongoing;
                }
                return rowIndex;
            } else {
                throw new Connect4Exception("Column is full. Choose another column.");
            }
        } else if (status == Status.PlayerOneWin) {
            throw new Connect4Exception("Player 1 has already won. Please start a new game.");
        } else if (status == Status.PlayerTwoWin) {
            throw new Connect4Exception("Player 2 has already won. Please start a new game.");
        } else if (status == Status.Draw) {
            throw new Connect4Exception("The game has ended in a draw. Please start a new game.");
        } else {
            throw new Connect4Exception("Fatal error: invalid game status.");
        }
    }

    boolean isHorizontalConnect4(Player player, int columnIndex, int rowIndex) {
        int count = 1;
        // count to left
        for (int i = columnIndex - 1; i >= 0; i--) {
            if (board[i][rowIndex] == player) {
                count++;
            } else {
                break;
            }
        }
        // count to right
        for (int i = columnIndex + 1; i < getColumnCount(); i++) {
            if (board[i][rowIndex] == player) {
                count++;
            } else {
                break;
            }
        }
        return count >= 4;
    }

    boolean isVerticalConnect4(Player player, int columnIndex, int rowIndex) {
        int count = 1;
        // count to top
        for (int i = rowIndex - 1; i >= 0; i--) {
            if (board[columnIndex][i] == player) {
                count++;
            } else {
                break;
            }
        }
        // count to bottom
        for (int i = rowIndex + 1; i < getRowCount(); i++) {
            if (board[columnIndex][i] == player) {
                count++;
            } else {
                break;
            }
        }
        return count >= 4;
    }

    boolean isLeftDiagonalConnect4(Player player, int columnIndex, int rowIndex) {
        int count = 1;
        // count to bottom right
        int i = rowIndex + 1;
        int j = columnIndex + 1;
        while (i < getRowCount() && j < getColumnCount()) {
            if (board[j][i] == player) {
                count++;
            } else {
                break;
            }
            i++;
            j++;
        }
        // count to top left
        i = rowIndex - 1;
        j = columnIndex - 1;
        while (i >= 0 && j >= 0) {
            if (board[j][i] == player) {
                count++;
            } else {
                break;
            }
            i--;
            j--;
        }
        return count >= 4;
    }

    boolean isRightDiagonalConnect4(Player player, int columnIndex, int rowIndex) {
        int count = 1;
        // count to bottom left
        int i = rowIndex + 1;
        int j = columnIndex - 1;
        while (i < getRowCount() && j >= 0) {
            if (board[j][i] == player) {
                count++;
            } else {
                break;
            }
            i++;
            j--;
        }
        // count to top right
        i = rowIndex - 1;
        j = j = columnIndex + 1;
        while (i >= 0 && j < getColumnCount()) {
            if (board[j][i] == player) {
                count++;
            } else {
                break;
            }
            i--;
            j++;
        }
        return count >= 4;
    }

    boolean isAllColumnFull() {
        boolean isFull = true;
        for (int i = 0; i < board.length; i++) {
            Player[] column = board[i];
            if (column[0] == null) {
                isFull = false;
                break;
            }
        }
        return isFull;
    }
}
